#include "tocken.h"

#define M_TYPE_SIMPLE 0	/* no value or parm */
#define M_TYPE_VALUE 1	/* with value, but no parm */
#define M_TYPE_PARM 2	/* with value and parm */

#define PV_TYPE_STRING 0
#define PV_TYPE_PARM 1

typedef struct _MacroValue {
	int type;
	DString *value;
	struct _MacroValue * next;
} MacroValue;

typedef struct _Macro {
	int type;
	MacroValue *mv;
} Macro;

typedef struct _KW_Pair {
	int kw;
	char *word;
} KW_Pair;

typedef struct _Sym_Pair {
	int sym;
	char *word;
} Sym_Pair;

static const KW_Pair keywords[] = {
	{KW_attribute, "attribute"},
	{KW_const, "const"},
	{KW_uniform, "uniform"},
	{KW_varying, "varying"},
	{KW_precision, "precision"},
	{KW_lowp, "lowp"},
	{KW_mediump, "mediump"},
	{KW_highp, "highp"},
	{KW_invariant, "invariant"},
	{KW_float, "float"},
	{KW_int, "int"},
	{KW_void, "void"},
	{KW_bool, "bool"},
	{KW_sampler2D, "sampler2D"},
	{KW_samplerCube, "samplerCube"},
	{KW_mat2, "mat2"},
	{KW_mat3, "mat3"},
	{KW_mat4, "mat4"},
	{KW_vec2, "vec2"},
	{KW_vec3, "vec3"},
	{KW_vec4, "vec4"},
	{KW_ivec2, "ivec2"},
	{KW_ivec3, "ivec3"},
	{KW_ivec4, "ivec4"},
	{KW_bvec2, "bvec2"},
	{KW_bvec3, "bvec3"},
	{KW_bvec4, "bvec4"},
	{KW_struct, "struct"},
	{KW_true, "KW_true"},
	{KW_false, "KW_false"},
	{KW_break, "KW_break"},
	{KW_continue, "continue"},
	{KW_do, "do"},
	{KW_for, "for"},
	{KW_while, "while"},
	{KW_if, "if"},
	{KW_else, "else"},
	{KW_in, "in"},
	{KW_out, "out"},
	{KW_inout, "inout"},
	{KW_discard, "discard"},
	{KW_return, "return"},
};

/* only one char symbol seen by syntax analyzer */
static const Sym_Pair Symbols[] = {
	{SYM_dot, '.'}
	{SYM_plus, '+'},
	{SYM_minus, '-'},
	{SYM_divide, '/'},
	{SYM_multiply, '*'},
	{SYM_mod, '%'},
	{SYM_less, '<'},
	{SYM_more, '>'},
	{SYM_lsquare, '['},
	{SYM_rsquare, ']'},
	{SYM_lround, '('},
	{SYM_rround, ')'},
	{SYM_lbrace, '{'},
	{SYM_rbrace, '}'},
	{SYM_bxor, '^'},
	{SYM_bor, '|'},
	{SYM_band, '&'},
	{SYM_bnot, '~'},
	{SYM_assign, '='},
	{SYM_not, '!'},
	{SYM_colon, ':'},
	{SYM_end, ';'},
	{SYM_comma, ','},
	{SYM_question, '?'},
};

static Token tk_line = {
	.type = TOKEN_TYPE_CONST_INT;
};

static Token tk_file = {
	.type = TOKEN_TYPE_CONST_INT;
};

static Token tk_version = {
	.type = TOKEN_TYPE_CONST_INT;
};

static Token tk_gl_es = {
	.type = TOKEN_TYPE_CONST_INT;
};

void token_free(Token *tk)
{
	switch (tk->type) {
	case TOKEN_TYPE_ID:
		dstring_free(tk->u.id.name);
		break;
	}
	free(tk);
}

static char get_char(Source *src)
{
	if (src->curr_str_num >= src->count)
		return '\0';
	if ((src->length && (src->curr_cnt >= src->length[src->curr_str_num])) ||
	    (src->string[src->curr_str_num][++src->curr_cnt] == '\0')) {
		src->curr_str_num++;
		src->curr_cnt = 0;
		if (src->curr_str_num >= src->count)
			return '\0';
	}
	return src->string[src->curr_str_num][++src->curr_cnt];
}

#define IS_ID_HEAD(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) == '_'))
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ID_CHAR(c) (IS_ID_HEAD(c) || IS_NUM(c))

static char next_line(Source *src)
{
	char cur_char;
	int hit = 0;
	cur_char = get_char(src);
	while (!hit) {
		switch (cur_char) {
		case '\r':
			cur_char = get_char(src);
			if (cur_char == '\n')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			hit = 1;
			break;
		case '\n':
			cur_char = get_char(src);
			if (cur_char == '\r')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			hit = 1;
			break;
		case '\0':
			hit = 1;
			break;
		}
	}
	return cur_char;
}

static char skip_comment(Source *src, int *first_word)
{
	char cur_char;
	int hit = 0;
	cur_char = get_char(src);
	while (!hit) {
		switch (cur_char) {
		case '\r':
			cur_char = get_char(src);
			if (cur_char == '\n')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			*first_word = 1;
			break;
		case '\n':
			cur_char = get_char(src);
			if (cur_char == '\r')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			*first_word = 1;
			break;
		case '*':
			cur_char = get_char(src);
			if (cur_char == '/') {
				cur_char = get_char(src);
				hit = 1;
			}
			break;
		case '\0':
			hit = 1;
			break;
		}
	}
	return cur_char;
}

/* stkip space and new line
 * first_word: next word is the first word of one line
 */
static char skip_space(Source *src, int *first_word)
{
	int hit = 0;
	char cur_char;

	*first_word = 0;
	if (src->next_char) {
		cur_char = src->next_char;
		src->next_char = '0';
	} else
		cur_char = get_char(src);
	while (!hit) {
		/* stkip space and new line */
		switch (cur_char) {
		case ' ':
		case '\t':
		case '\v':
		case '\f':
			cur_char = get_char(src);
			break;
		case '\r':
			cur_char = get_char(src);
			if (cur_char == '\n')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			*first_word = 1;
			break;
		case '\n':
			cur_char = get_char(src);
			if (cur_char == '\r')
				cur_char = get_char(src);
			tk_line->u.const_int.value++;
			*first_word = 1;
			break;
		case '/':
			cur_char = get_char(src);
			if (cur_char == '/') {
				cur_char = next_line(src);
				*first_word = 1;
			} else if (cur_char == '*')
				cur_char = skip_comment(src, first_word);
			break;
		default: /* it's a word, or source end */
			hit = 1;
			break;
		}
	}
	return cur_char;
}

static char directive_line (Source *src)
{
	char cur_char;
	int first_word = 0;
	cur_char = skip_space(src, &first_word);
	
}

/* for syntax analyser */
Token* src_next_token(Source *src)
{
	char cur_char;
	int first_word = 0, hit = 0;

	cur_char = skip_space(src, &first_word);
	if (!cur_char)
		return NULL;
	if (first_word && cur_char == '#') { /* preprocess directive line */
	}

	if (IS_ID_HEAD(cur_char)) { /* geting id */
	} else if (IS_NUM(cur_char)) { /*geting constant */
	} else {
	}
}

